//
//  NSXMLParser.m
//  iHealthEHR
//
//  Created by Joseph Ayo Akinyele on 3/21/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "XMLParser.h"

@implementation XMLParser
@synthesize tag;

-(id)init
{
    if (self = [super init])
    {
		self.tag = [[NSMutableDictionary alloc] init];
    }
    return self;
}

+(GDataContinuityOfCareRecord *) createCCRDocument:(NSXMLElement *) element 
{
	// NSString *sample = @"<ContinuityOfCareRecord xmlns='urn:astm-org:CCR'><CCRDocumentObjectID>AjdkfeLAA</CCRDocumentObjectID><Body><Problems><Problem><CCRDataObjectID>BB0005</CCRDataObjectID><DateTime><Type><Text>Date Updated</Text></Type><ExactDateTime>2010-03-25T00:18:58Z</ExactDateTime></DateTime><Type><Text>Problem</Text></Type><Description><Text>Encrypted - Seizures</Text><Code><Value>C0009952</Value><CodingSystem>UMLS Concept</CodingSystem><Version>2005</Version></Code><Code><Value>780.31</Value><CodingSystem>ICD9CM</CodingSystem><Version>2006</Version></Code><Code><Value>41497008</Value><CodingSystem>SNOMEDCT</CodingSystem><Version>2005</Version></Code></Description><Source><Actor><ActorID>AA0001akslkajsdlfkajsdlfakjsdflaaksjdfalasdfasdf</ActorID></Actor></Source></Problem></Problems></Body></ContinuityOfCareRecord>";
/*	NSString *sample = @"<ContinuityOfCareRecord><CCRDocumentObjectID>AjdkfeLAA</CCRDocumentObjectID><Body>"
	"<Problems>"
	"<Source><Actor><ActorID>Parent:Problems</ActorID><ActorRole><Text>ICAgICAgIDxEZXNjcmlwdGlvbj4KICAgICAgICAgIDxUZXh0PkVuY3J5"
	"cHRlZCAtIFNlaXp1cmVzPC9UZXh0PgogICAgICAgICAgPENvZGU+CiAgICAgICAgICAgIDxWYWx1ZT5DMDAwOTk1MjwvVmFsdWU+CiAgICAgICAgICAgIDxD"
	"b2RpbmdTeXN0ZW0+VU1MUyBDb25jZXB0PC9Db2RpbmdTeXN0ZW0+CiAgICAgICAgICAgIDxWZXJzaW9uPjIwMDU8L1ZlcnNpb24+CiAgICAgICAgICA8L0Nv"
	"ZGU+CiAgICAgICAgICA8Q29kZT4KICAgICAgICAgICAgPFZhbHVlPjc4MC4zMTwvVmFsdWU+CiAgICAgICAgICAgIDxDb2RpbmdTeXN0ZW0+SUNEOUNNPC9D"
	"b2RpbmdTeXN0ZW0+CiAgICAgICAgICAgIDxWZXJzaW9uPjIwMDY8L1ZlcnNpb24+CiAgICAgICAgICA8L0NvZGU+CiAgICAgICAgICA8Q29kZT4KICAgICAg"
	"ICAgICAgPFZhbHVlPjQxNDk3MDA4PC9WYWx1ZT4KICAgICAgICAgICAgPENvZGluZ1N5c3RlbT5TTk9NRURDVDwvQ29kaW5nU3lzdGVtPgogICAgICAgICAg"
	"ICA8VmVyc2lvbj4yMDA1PC9WZXJzaW9uPgogICAgICAgICAgPC9Db2RlPgogICAgICAgPC9EZXNjcmlwdGlvbj4K</Text></ActorRole></Actor></Source>"
	"</Problem></Problems></Body></ContinuityOfCareRecord>";
*/	
	// NSError *error = nil;
	NSXMLElement *ccrXML = element; // [[[NSXMLElement alloc] initWithXMLString:sample error:&error] autorelease]; 
	// NSLog(@"About to upload: '%@'", ccrXML);
	
	GDataContinuityOfCareRecord *ccr = [[GDataContinuityOfCareRecord alloc] init]; 
	
	NSArray *copyOfChildren = [GDataUtilities arrayWithCopiesOfObjectsInArray:[ccrXML children]]; 
	[ccr setChildXMLElements:copyOfChildren];
	
	NSDictionary *ccrDefaultNS = [NSDictionary 
								  dictionaryWithObject:kGDataNamespaceCCR 
								  forKey:@""]; 
	[ccr setNamespaces:ccrDefaultNS];
	
	return ccr;
}

// start with tag w/ search name. Then descend the tree looking for 'Text' attributes.
// Once found, enlist parent into 
-(void) parseDemographics:(NSString *) searchEntry fromElement:(NSXMLElement *) element
{		
	NSXMLNode *tmp;
	NSString *key, *value;

	if([searchEntry isEqualToString:@"VitalSigns"]) {
		// section provides Blood type, height, weight.
		NSArray *n = [element elementsForName:@"Body"];
		NSXMLNode *e = [n objectAtIndex:0];	
		NSArray *test = [XMLParser findChild:e withTag:@"Test"];
		
		for(int i = 0; i < [test count]; i++) {
			NSXMLNode *t = [test objectAtIndex:i];
			NSArray *t1 = [XMLParser findChild:t withTag:@"Description"];
			NSArray *t2 = [XMLParser findChild:t withTag:@"TestResult"];

			NSEnumerator *e1 = [[[t1 objectAtIndex:0] children] objectEnumerator];
			NSEnumerator *e2 = [[[t2 objectAtIndex:0] children] objectEnumerator];			
			while(tmp = [e1 nextObject]) {
				if([[tmp name] isEqualToString:@"Text"]) {
					key = [tmp stringValue];
					//NSLog(@"Node key => %@", key);
				}
			}
			
			while(tmp = [e2 nextObject]) {
				if([[tmp name] isEqualToString:@"Value"]) {
					value = [tmp stringValue];
					//NSLog(@"Node value => %@", value);
				 }
			}
			
			// add to dictionary
			[self.tag setObject:value forKey:key];
			// clean-up memory
			[t1 release];
			[t2 release];
		}
		// clean-up memory
		[test release];
	}
	else if([searchEntry isEqualToString:@"Demographics"]) {
		// section provides DOB and sex (male or female)
		NSArray *n = [element elementsForName:@"Actors"];
		NSXMLNode *e = [n objectAtIndex:0];	
	
		NSArray *t1 = [XMLParser findChild:e withTag:@"Person"];
		
		NSEnumerator *e1 = [[[t1 objectAtIndex:0] children] objectEnumerator];
		while(tmp = [e1 nextObject]) {
			if([[tmp name] isEqualToString:@"DateOfBirth"]) {
				//NSLog(@"Node value => %@", [tmp stringValue]);
				[self.tag setObject:[tmp stringValue] forKey:@"DOB"];
			}
			else if([[tmp name] isEqualToString:@"Gender"]) {
				// NSLog(@"Node value => %@", [tmp stringValue]);
				[self.tag setObject:[tmp stringValue] forKey:[tmp name]];
			}
		}
		// [n release];
		[t1 release];
	}
	else if([searchEntry isEqualToString:@"FunctionalStatus"]) {
		// not interested in this data, 
	}
	else {
		// SocialHistory is last, with most character in title which sucks for string comparison!
		// provides race and
		NSArray *n = [element elementsForName:@"Body"];
		NSXMLNode *e = [n objectAtIndex:0];	
		//NSArray *t1 = [self findChild:e withTag:@"Type"];
		NSArray *t1 = [XMLParser findChild:e withTag:@"Description"];
		key = @"Race";
		value = [[t1 objectAtIndex:0] stringValue];
		
		// NSLog(@"Node key = '%@' => '%@'", key, value);
		[self.tag setObject:value forKey:key];
		// [n release];
		[t1 release];
	}
}

+(BOOL) searchForTag: (NSXMLNode *) node withName: (NSString *) name orWith: (NSString *) othername
{
	int top = 0, numChild = 0;
	NSXMLNode *nodes = node;
	NSXMLNode *cur_ptr, *tmp;
	NSEnumerator *e;
	NSMutableArray *_stack = [[NSMutableArray alloc] init];
	BOOL _found = NO;
	
	if([nodes childCount] > 0) {
		e = [[nodes children] objectEnumerator];
		while(cur_ptr = [e nextObject])
			// build up stack
			[_stack addObject:cur_ptr];
	}

	// while stack not empty do the following
	while([_stack count] > 0) {
		// pop last element of the stack and visit node and determine if equal to 'name'
		top = [_stack count];
		if(top > 0) {
			cur_ptr = [[_stack objectAtIndex:(top-1)] retain];
			[_stack removeLastObject];
			//NSLog(@"Visiting popped node = %@", [cur_ptr name]);
		}	
		
		// if we find a match, we conclude the tag exists and can exit.
		// NSLog(@"cur_ptr value: '%@' =?= '%@'", [cur_ptr stringValue], othername);
		if([[cur_ptr name] isEqualToString:name]) {
			_found = YES;
			break;
		}		
		else if([[cur_ptr stringValue] isEqualToString:othername]) {
			_found = YES;
			break;
		}
		
		numChild = [cur_ptr childCount];
		if(numChild > 0) {
			e = [[cur_ptr children] objectEnumerator];
			while(tmp = [e nextObject])
				// build up stack
				[_stack addObject:tmp];
		}
		
		// okay to release references in cur_ptr?
		[cur_ptr release];
	}	
	
	[_stack release];	
	return _found;
}

// Uses a stack to find child tags in the DOM. Builds up an array of all the 
// nodes found in DOM and returns to user. Caller must free array returned.
+(NSArray *) findChild: (NSXMLNode *) node withTag: (NSString *) name
{
	int top = 0, numChild = 0;
	NSXMLNode *nodes = node;
	NSXMLNode *cur_ptr, *tmp;
	NSEnumerator *e;
	NSMutableArray *_stack = [[NSMutableArray alloc] init];
	NSMutableArray *_found = [[NSMutableArray alloc] init];
	
	if([nodes childCount] > 0) {
		e = [[nodes children] objectEnumerator];
		while(cur_ptr = [e nextObject])
			// build up stack
			[_stack addObject:cur_ptr];
	}
	
	// while stack not empty do the following
	while([_stack count] > 0) {
		// pop last element of the stack and visit node and determine if equal to 'name'
		top = [_stack count];
		if(top > 0) {
			cur_ptr = [[_stack objectAtIndex:(top-1)] retain];
			[_stack removeLastObject];
			//NSLog(@"Visiting popped node = %@", [cur_ptr name]);
		}	
		
		// if during visit, node happens to have children, push the children onto the stack
		if([[cur_ptr name] isEqualToString:name]) {
			[_found addObject:cur_ptr];
		}
		
		numChild = [cur_ptr childCount];
		if(numChild > 0) {
			e = [[cur_ptr children] objectEnumerator];
			while(tmp = [e nextObject])
				// build up stack
				[_stack addObject:tmp];
		}
		
		// okay to release references in cur_ptr?
		[cur_ptr release];
	}	
	
	[_stack release];
	if([_found count] == 0) {
		[_found release];
		return nil;
	}
		
	return _found;
}

+(BOOL) findAndReplace:(NSXMLElement *) origElement withName:(NSString *) name toThis:(NSXMLElement *) newElement
{
	NSArray *n = [origElement elementsForName:@"Body"];
	NSXMLNode *node;
	NSXMLElement *parent;
	NSXMLNode *cur_ptr, *tmp;
	int top = 0, numChild = 0;
	NSEnumerator *e;
	BOOL _found = NO;
	if([n count] > 0)
		node = [n objectAtIndex:0];
	else {
		return _found;
	}

	// match the 'name' tag first. Once you find it, remove it and put newElement in its place.
	NSMutableArray *_stack = [[NSMutableArray alloc] init];	
	if([node childCount] > 0) {
		e = [[node children] objectEnumerator];
		while(cur_ptr = [e nextObject])
			// build up stack
			[_stack addObject:cur_ptr];
	}
	
	// while stack not empty do the following
	while([_stack count] > 0) {
		// pop last element off the stack and visit node and determine if equal to 'name'
		top = [_stack count];
		if(top > 0) {
			cur_ptr = [[_stack objectAtIndex:(top-1)] retain];
			[_stack removeLastObject];
			// NSLog(@"Visit popped node = %@", [cur_ptr name]);
		}	
		
		// if during visit, node happens to have children, push the children onto the stack
		if([[cur_ptr name] isEqualToString:name]) {
			// NSLog(@"Found '%@' and parent is currently set to '%@'?", name, [parent name]);
			[parent removeChild:cur_ptr];
			[parent addChild:(NSXMLNode *)newElement];
			_found = YES;
		}
		
		numChild = [cur_ptr childCount];
		if(numChild > 0) {
			e = [[cur_ptr children] objectEnumerator];
			while(tmp = [e nextObject]) {
				// peek and see if we find node.
				// if yes, then set parent to cur_ptr, since we are pushing its children onto the stack
				if([[tmp name] isEqualToString:name]) {
					parent = (NSXMLElement *)cur_ptr;
				}
				// build up stack
				[_stack addObject:tmp];
			}
		}		
		// okay to release references in cur_ptr?
		[cur_ptr release];
	}	
	
	[_stack release];		
	return _found;
}

// caller must free NSXMLElement returned
+(NSXMLElement *) findAndReturnParent:(NSXMLElement *) element withName:(NSString *) name 
{
	NSArray *n = [element elementsForName:@"Body"];
	NSXMLNode *node;
	NSString *parent;
	NSError *error = nil;
	NSXMLNode *cur_ptr, *tmp;
	int top = 0, numChild = 0;
	NSEnumerator *e;
	if([n count] > 0)
		node = [n objectAtIndex:0];
	else {
		return nil;
	}
	NSXMLElement *found = // [NSXMLNode elementWithName:@"ABE"];
		[[NSXMLElement alloc] initWithXMLString:@"<ABEData></ABEData>" error:&error];
	// match the 'name' tag first. Once you find it, remove it and put newElement in its place.
	NSMutableArray *_stack = [[NSMutableArray alloc] init];	
	if([node childCount] > 0) {
		e = [[node children] objectEnumerator];
		while(cur_ptr = [e nextObject])
			// build up stack
			[_stack addObject:cur_ptr];
	}
	
	// while stack not empty do the following
	while([_stack count] > 0) {
		// pop last element off the stack and visit node and determine if equal to 'name'
		top = [_stack count];
		if(top > 0) {
			cur_ptr = [[_stack objectAtIndex:(top-1)] retain];
			[_stack removeLastObject];
			// NSLog(@"Visit popped node = %@", [cur_ptr name]);
		}	
		
		// if during visit, node happens to have children, push the children onto the stack
		if([[cur_ptr name] isEqualToString:name]) {
			// NSLog(@"Found '%@' and parent is currently set to '%@'?", name, [parent name]);
			//[found removeChild:cur_ptr];
			[found addChild:cur_ptr];
			[found addAttribute:[NSXMLNode elementWithName:@"Parent" stringValue:parent]];
			// NSLog(@"What we found: XML => '%@'", [found XMLString]);
		}
		
		numChild = [cur_ptr childCount];
		if(numChild > 0) {
			e = [[cur_ptr children] objectEnumerator];
			while(tmp = [e nextObject]) {
				// peek and see if we find node.
				// if yes, then set parent to cur_ptr, since we are pushing its children onto the stack
				if([[tmp name] isEqualToString:name]) {
					parent = [cur_ptr name];
				}
				// build up stack
				[_stack addObject:tmp];
			}
		}		
		// okay to release references in cur_ptr?
		[cur_ptr release];
	}	
	
	[_stack release];		
	return found;	
}

+(BOOL) AddChildToNode:(NSXMLElement *) origElement withName:(NSString *) name withThis:(NSXMLElement *) newElement
{
	NSArray *n = [origElement elementsForName:@"Body"];
	NSXMLNode *node;
	NSXMLNode *cur_ptr, *tmp;
	int top = 0, numChild = 0;
	NSEnumerator *e;
	if([n count] > 0)
		node = [n objectAtIndex:0];
	else {
		return NO;
	}
	// match the 'name' tag first. Once you find it, remove it and put newElement in its place.
	NSMutableArray *_stack = [[NSMutableArray alloc] init];	
	if([node childCount] > 0) {
		e = [[node children] objectEnumerator];
		while(cur_ptr = [e nextObject])
			// build up stack
			[_stack addObject:cur_ptr];
	}
	
	// while stack not empty do the following
	while([_stack count] > 0) {
		// pop last element off the stack and visit node and determine if equal to 'name'
		top = [_stack count];
		if(top > 0) {
			cur_ptr = [[_stack objectAtIndex:(top-1)] retain];
			[_stack removeLastObject];
			// NSLog(@"Visit popped node = %@", [cur_ptr name]);
		}	
		
		// if during visit, node happens to have children, push the children onto the stack
		if([[cur_ptr name] isEqualToString:name]) {
			// NSLog(@"Found '%@' and parent is currently set to '%@'?", name, [parent name]);
			NSXMLElement *found = (NSXMLElement *) cur_ptr;
			[found addChild:(NSXMLNode *) newElement];
			// NSLog(@"DEBUG: XML is now => '%@'", [found XMLString]);
			NSLog(@"Added child element to '%@'", name);
		}
		
		numChild = [cur_ptr childCount];
		if(numChild > 0) {
			e = [[cur_ptr children] objectEnumerator];
			while(tmp = [e nextObject]) {
				[_stack addObject:tmp];
			}
		}		
		// okay to release references in cur_ptr?
		[cur_ptr release];
	}	
	
	[_stack release];		
	return YES;	
	
}

- (void)dealloc {
	[tag release];
	[super dealloc];
}

@end
